package com.tian.auto.model;

import com.tian.auto.Constants;
import com.tian.auto.Log;
import com.tian.auto.StringUtils;
import com.tian.auto.info.ResData;
import com.tian.auto.parse.PathParseProcessor;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * 更新string值或者string-array值的model
 */
public abstract class AbsUpdateModel {

    private final static String TAG = AbsUpdateModel.class.getSimpleName();

    protected ResData resData;

    protected String targetKey;

    protected List<String> xmlFilesPath;

    /**
     * string-array中，要修改的下标，如果这个值isEmpty的话，说明要更改的是string而非string-array
     */
    protected int arrayIndex = -1;

    public AbsUpdateModel(ResData resData, HashMap<String, String> targetMap, List<String> xmlFilesPath) {
        this.resData = resData;
        this.targetKey = targetMap.get(Constants.MapKey.KEY_NAME);
        this.xmlFilesPath = xmlFilesPath;

        if (targetMap.containsKey(Constants.MapKey.ARRAY_INDEX)) {
            this.arrayIndex = Integer.parseInt(targetMap.get(Constants.MapKey.ARRAY_INDEX));
        }
    }

    /**
     * 更新string的值
     *
     * @param document 文档
     * @param element  元素
     * @param path     当前遍历的文件
     * @throws
     */
    public abstract void updateStringValues(Document document, Element element, String path) throws Exception;

    /**
     * 创建xml并且写入string值
     */
    public abstract void createXmlAndInsertStringValues(File file) throws Exception;

    /**
     * 更新string-array的值
     */
    public abstract void updateStringArrayValues(Document document, Element element, String path) throws Exception;

    /**
     * 创建xml并且写入string-array值
     */
    public abstract void createXmlAndInsertStringArrayValues(File file) throws Exception;

    /**
     * 更新资源
     */
    public final void executeUpdateValues(boolean isNeedMatchModuleName) throws Exception {
        IUpdateValues iUpdateValues = new IUpdateValues() {
            @Override
            public void updateValues(Document document, Element element, String filePath) throws Exception {
                if (arrayIndex == -1)
                    updateStringValues(document, element, filePath);
                else
                    updateStringArrayValues(document, element, filePath);
            }

            @Override
            public void createValues(File file) throws Exception {
                if (arrayIndex == -1)
                    createXmlAndInsertStringValues(file);
                else
                    createXmlAndInsertStringArrayValues(file);
            }

            @Override
            public String elementName() {
                return arrayIndex == -1 ? Constants.ElementValues.ELEMENT_STRING : Constants.ElementValues.ELEMENT_STRING_ARRAY;
            }
        };

        executeUpdateValues(iUpdateValues, isNeedMatchModuleName);
    }

    /**
     * 根据 键的名字 遍历指定目录下xml中是否含有 这个键
     * <p>
     * 1.如果含有这个 键如 "print_dialog",那么比对其值，值与ResData中定义的相同就不更改，不相同就更改。更改完成后，更新ResData中的键如"print_dialog",具体存在与哪个xml文件中<br>
     * 2.如果不含有这个键 如"print_dialog",那么添加一个<string name="print_dialog">xxxx</> <br>
     * 3.如果在values-en或values-zh-rTW目录下都没有对应都xml文件，那么创建xml文件
     * </p>
     *
     * @throws
     */
    private void executeUpdateValues(IUpdateValues iUpdateValues, boolean isNeedMatchModuleName) throws Exception {
        //说明已经遍历过 "中文环境" 下 键值对了，在英文 ，繁体中文下不再需要去从头遍历
        if (!StringUtils.isEmpty(resData.getCnValuePath())) {
            executeNotCnUpdateValues(iUpdateValues);
            return;
        }

        executeCnUpdateValues(iUpdateValues, isNeedMatchModuleName);
    }

    /**
     * 遍历cn环境
     *
     * @param iUpdateValues
     * @param isNeedMatchModuleName 是否需要匹配module名，一般情况需要，但是在翻译离线模式的app时，因为app结构的module名和文档的不能匹配所以选择不匹配moduleName
     */
    private void executeCnUpdateValues(IUpdateValues iUpdateValues, boolean isNeedMatchModuleName) throws Exception {

        //在xml文件目录中的文件里  是否包含有 stringKey;
        boolean isKeyInFiles = false;

        for (String path : xmlFilesPath) {
            if (StringUtils.isEmpty(path))
                continue;

            SAXReader reader = new SAXReader();
            Document document = reader.read(new File(path));
            //获取根节点
            Element rootElement = document.getRootElement();
            //获取根节点下,名为string的子节点迭代器
            Iterator<Element> it = rootElement.elementIterator(iUpdateValues.elementName());
            //遍历迭代器
            while (it.hasNext()) {
                // 如果已经遍历到了 对应的string的key（代表已经进行过，修改，添加）
                if (isKeyInFiles && isNeedMatchModuleName) break;

                Element element = it.next();

                if (!hasKey(element.attributes(), targetKey))
                    continue;

                //如果string节点的 name属性值 为stringKey
                isKeyInFiles = true;
                //更新数据
                iUpdateValues.updateValues(document, element, path);
            }

            if (isKeyInFiles && isNeedMatchModuleName) break;
        }

        if (!isKeyInFiles) {
            if (StringUtils.isEmpty(resData.getCnValuePath())) {
                Log.info(TAG, "格式错误，在values中都没有key=" + targetKey + "的字符串或字符数组");
                Constants.addErrorList("格式错误，在values中都没有key=" + targetKey + "的字符串或字符数组");
                return;
            }
        }
    }

    /**
     * 非cn环境下的遍历
     *
     * @param iUpdateValues
     */
    private void executeNotCnUpdateValues(IUpdateValues iUpdateValues) throws Exception {
        ArrayList<String> targetPathArray = new ArrayList<>();

        for (String itemPath : resData.getCnValuePath().split(",")) {
            if (this instanceof EnUpdateModel) {
                //检测在values-en下对应的 string_xxx.xml文件是否存在,不存在就需要创建
                targetPathArray.add(itemPath.replace("values/", "values-en/"));
            } else if (this instanceof ZhrTWUpdateModel) {
                targetPathArray.add(itemPath.replace("values/", "values-zh-rTW/"));
            } else if (this instanceof ThrTHUpdateModel) {
                targetPathArray.add(itemPath.replace("values/", "values-th-rTH/"));
            }
        }

        if (targetPathArray.isEmpty())
            return;

        for (String target : targetPathArray) {
            File targetFile = new File(target);
            if (targetFile.exists()) {
                //在xml文件目录中的文件里  是否包含有 stringKey;
                boolean isKeyInFile = false;
                SAXReader reader = new SAXReader();
                Document document = reader.read(targetFile);
                //获取根节点
                Element rootElement = document.getRootElement();
                //获取根节点下,名为string的子节点迭代器
                Iterator<Element> it = rootElement.elementIterator(iUpdateValues.elementName());
                //遍历迭代器
                while (it.hasNext()) {
                    Element element = it.next();

                    if (!hasKey(element.attributes(), targetKey))
                        continue;

                    isKeyInFile = true;
                    //更新数据
                    iUpdateValues.updateValues(document, element, target);
                    break;
                }

                if (!isKeyInFile) {
                    iUpdateValues.createValues(targetFile);
                }
            } else {
                iUpdateValues.createValues(targetFile);
            }
        }
    }

    /**
     * 同步数据到 磁盘中
     *
     * @param path
     * @param document
     * @throws
     */
    public void saveUpdateToXml(String path, Document document) throws Exception {
        //用于格式化xml内容和设置头部标签
//        OutputFormat format = OutputFormat.createPrettyPrint();
        //设置xml文档的编码为utf-8
//        format.setEncoding("utf-8");

        File targetFile = new File(path);
        boolean isExist = targetFile.exists();

        if (!isExist) {

            //检测  父目录存在 与否 如果不存在创建
            ///Users/tianjinyong/Desktop/work/international/app/src/main/res-shop/values-zh-rTW/strings_print.xm
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }

            targetFile.createNewFile();
        }

        // 注意：XML文件是被加载到内存中 修改也是在内存中 ==》因此需要将内存中的数据同步到磁盘中
//        XMLWriter writer = new XMLWriter(new FileWriter(path), format);//将内存数据关联给一个字符输出流
        XMLWriter writer = new XMLWriter(new FileWriter(path));
        writer.write(document);
        writer.close();

        //如果创建了文件那么需要更新字典数据
        if (!isExist) {
            PathParseProcessor.initProjectStringPathMap();
        }
    }

    /**
     * 提供的对更新的操作
     */
    public interface IUpdateValues {

        /**
         * 更新数据
         *
         * @param document
         * @param element
         * @param filePath
         */
        void updateValues(Document document, Element element, String filePath) throws Exception;

        /**
         * 创建数据
         */
        void createValues(File file) throws Exception;

        /**
         * 获取要遍历的根节点下子节点名字，目前有string，string-array
         *
         * @return
         */
        String elementName();
    }

    /**
     * 判断属性列表中 是否含有 name=targetkey 的属性
     *
     * @param attributes
     * @param targetKey
     * @return
     */
    public boolean hasKey(List<Attribute> attributes, String targetKey) {
        if (attributes == null || attributes.isEmpty())
            return false;

        boolean hasKey = false;

        for (Attribute item : attributes) {
            if ("name".equals(item.getName()) && targetKey.equals(item.getValue())) {
                hasKey = true;
            }
        }

        return hasKey;
    }

}
